﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MetadataBasedConfigArch.Framework.Model;
using System.ComponentModel;

namespace MetadataBasedConfigArch.Framework.Processor
{
    public abstract class AbstractConfigurableProcessor<T> : IConfigurableProcessor<T>
        where T : IConfigurable
    {
        #region Ctors

        public AbstractConfigurableProcessor()
        { 
        }

        public AbstractConfigurableProcessor(T configurable, IMetadataBasedEntityProvider entityProvider)
            :base()
        {
            Configurable = configurable;
            EntityProvider = entityProvider;
        }

        #endregion

        #region Implement IConfigurableProcessor<T> interface

        public T Configurable
        {
            get
            {
                return (T)TargetConfigurable;
            }
            set
            {
                TargetConfigurable = value;
            }
        }

        public IConfigurable TargetConfigurable
        {
            get;
            set;
        }

        public IMetadataBasedEntityProvider EntityProvider
        {
            get;
            set;
        }

        public virtual void LoadConfigurableInfos()
        {
            List<KeyValuePair<String, Object>> extendProperties = new List<KeyValuePair<String, Object>>();

            // Load ConfigMeta Information
            String configTypeName = Helper.GetConfigurableConfigTypeName<T>();

            Configurable.ConfigMetaInst = EntityProvider.GetConfigMeta(configTypeName, Configurable.ConfigMetaContent);

            var allConfigInfos = (from ci in Configurable.ConfigMetaInst.ConfigInfosInst.IncludeStaticConstraint()
                                  //where ci.TenantInst.ID == Configurable.TenantID
                                  select ci).ToList();

            Configurable.ConfigDatasInst = EntityProvider.GetConfigDatas(configTypeName, Configurable.TenantID, Configurable.ConfigMetaContent, Configurable.DataID)
                                    .ToList()
                                    .Select(cd => (IConfigMetaConfigData)cd)
                                    .ToList();

            foreach (var configInfo in allConfigInfos)
            {
                IConfigMetaConfigInfoProperty property = (from p in configInfo.ConfigInfoPropertiesInst.IncludeStaticConstraint()
                                                          where 
                                                          //p.TenantInst.ID == Configurable.TenantID && 
                                                          p.Key == Constants.PropertyKey_DataType
                                                          select p).FirstOrDefault();

                var configData = Configurable.ConfigDatasInst.Where(cd => cd.ConfigInfoInst.ID == configInfo.ID).FirstOrDefault();
                string configDataValue = configData == null ? string.Empty : configData.Value; 

                if (property != null)
                {
                    TypeConverter converter = TypeDescriptor.GetConverter(Type.GetType(property.Value));
                    if (converter.CanConvertFrom(typeof(string)))
                    {
                        if (converter.IsValid(configDataValue))
                        {
                            object value = converter.ConvertFromString(configDataValue);
                            extendProperties.Add(new KeyValuePair<String, Object>(configInfo.Content, value));
                        }
                        else
                        {
                            extendProperties.Add(new KeyValuePair<String, Object>(configInfo.Content, null));
                        }
                    }
                }
                else
                {
                    //Should Convert Value to data type value
                    extendProperties.Add(new KeyValuePair<String, object>(configInfo.Content, configDataValue));
                }

            } 
            Configurable.ExtendProperties = extendProperties;
        }

        public virtual void SetConfigurableInfo(string configContent, object value)
        {
            var exProperty = (from ep in Configurable.ExtendProperties where ep.Key == configContent select ep).FirstOrDefault();

            if (exProperty.Key == configContent) //Exists
            {
                //Assign related Persist Data (in DB)
                var configData = Configurable.ConfigDatasInst.Where(cd => cd.ConfigInfoInst.Content == configContent).FirstOrDefault();
                bool valueIsValid = true;
                foreach (var configInfoProperty in configData.ConfigInfoInst.ConfigInfoPropertiesInst)
                {
                    valueIsValid &= configInfoProperty.ValidateCanBeAssignableFrom(value);
                    if (!valueIsValid)
                    {
                        break;
                    }
                }

                if (valueIsValid)
                {
                    TypeConverter typeConverter = TypeDescriptor.GetConverter(value);
                    if (configData != null && typeConverter != null)
                    {
                        configData.Value = typeConverter.ConvertToString(value);
                    }

                    var extendProperties = Configurable.ExtendProperties.Where(ep => ep.Key != configContent).ToList();
                    extendProperties.Add(new KeyValuePair<string, object>(configContent, value));
                    Configurable.ExtendProperties = extendProperties;
                }
                else
                {
                    throw new InvalidOperationException("the value is not valid!");
                }
            }
        }

        public virtual object GetConfigurableInfo(string configContent)
        {
            return (from ep in Configurable.ExtendProperties where ep.Key == configContent select ep.Value).FirstOrDefault();
        }

        #endregion

        #region Abstract Methods

        //protected abstract List<IConfigMetaConfigData> LoadConfigDatas();         

        #endregion
    }
}
